package com.seabook.tankgame.client;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DecoratorPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class GWTTankGame implements EntryPoint {

	private static final int TANK_STEP_LENGTH = 10;
	private static final int BULLET_STEP_LENGTH = 30;

	private static final int INIT_X = 950;
	private static final int INIT_Y = 270;

	// This is for rendering the whole application
	// private VerticalPanel verticalPanel;

	// This is for rendering the tank
	private AbsolutePanel absolutePanel;

	// This is for rendering the command Button
	private HorizontalPanel horizontalPanel;

	private Button goRightButton;
	private Button goLeftButton;
	private Button goUpButton;
	private Button goDownButton;
	private Button fireButton;
	private Button inKeyboardCtrlMod;

	private int fireCount = 0;

	private static Map<String, Image> tankImages = new HashMap<String, Image>();

	private Tank tank;
	private List<Tank> badTanks = new ArrayList<Tank>();

	private static TankImageSources images;

	private ClickHandler handler = new CmdBtnClickHandler();
	private KeyDownHandler tankKeyDownHandler = new TankKeyDownHandler();
	private boolean gameover;

	Image fireImage = new Image(images.onFireImage());
	static {
		images = GWT.create(TankImageSources.class);
		tankImages.put("right", new Image(images.tankRight()));
		tankImages.put("left", new Image(images.tankLeft()));
		tankImages.put("down", new Image(images.tankDown()));
		tankImages.put("up", new Image(images.tankUp()));
		tankImages.put("bullet", new Image(images.bullet()));
		tankImages.put("fire", new Image(images.onFireImage()));
	}

	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {

		// init All the widgets in the UI

		// verticalPanel = new VerticalPanel();

		// 1) Abosulte Panel for adding Tanks

		absolutePanel = new AbsolutePanel();
		absolutePanel.setPixelSize(1024, 600);
		absolutePanel.setTitle("Tank Demo");
		absolutePanel.setVisible(true);

		images = GWT.create(TankImageSources.class);

		tank = new Tank(TankDirection.LEFT, false, INIT_X, INIT_Y,
				(Widget) tankImages.get("left"));
		tank.drawSelf(absolutePanel);

		// 1.2) add some bad guys
		badTanks = produceBadTanks(7);
		reDrawBadTanks();

		// 2) FlowPanel for adding Command Buttons
		horizontalPanel = new HorizontalPanel();

		goRightButton = new Button("go-right");
		goLeftButton = new Button("go-left");
		goUpButton = new Button("go-up");
		goDownButton = new Button("go-down");
		fireButton = new Button("fire");
		inKeyboardCtrlMod = new Button("KB Ctrl Mod!");

		horizontalPanel.add(goRightButton);
		horizontalPanel.add(goLeftButton);
		horizontalPanel.add(goUpButton);
		horizontalPanel.add(goDownButton);
		horizontalPanel.add(fireButton);
		horizontalPanel.add(inKeyboardCtrlMod);

		goRightButton.addClickHandler(handler);
		goLeftButton.addClickHandler(handler);
		goUpButton.addClickHandler(handler);
		goDownButton.addClickHandler(handler);
		fireButton.addClickHandler(handler);

		inKeyboardCtrlMod.addKeyDownHandler(tankKeyDownHandler);

		// We can add style names to widgets

		// Use RootPanel.get() to get the entire body element

		DecoratorPanel absolutePanelWrapper = new DecoratorPanel();
		absolutePanelWrapper.add(absolutePanel);

		RootPanel.get("animationCanvasContainer").add(absolutePanelWrapper);
		RootPanel.get("cmdButtonContainer").add(horizontalPanel);

		// Setup timer to reDraw bullet
		Timer refreshTimer = new Timer() {
			@Override
			public void run() {
				reDraw();
			}
		};

		refreshTimer.scheduleRepeating(100);
	}

	// produce and draw in the canvas as well;
	private List<Tank> produceBadTanks(int count) {
		System.out.println("GWTAnimation.produceBadTanks()");
		List<Tank> badTanks = new ArrayList<Tank>();
		for (int i = 0; i < count; i++) {
			Tank badTank = new Tank();
			TankDirection randomDirection = TankDirection
					.getRandomTankDirection();
			badTank.setTankDirection(randomDirection);
			badTank.setEvil(true);
			badTank.initEvilTankImages();
			// may rellocate the position for the bad tanks

			int initSeed = i;
			if (i % 2 == 0) {
				initSeed = -1 * i;
			}

			badTank.setX(50 * initSeed);
			badTank.setY(INIT_Y + 30 * initSeed);
			Widget tankImage = badTank.getBadTankImages().get(
					randomDirection.toString());
			badTank.setTankImage(tankImage);

			badTanks.add(badTank);
		}
		return badTanks;
	}

	private class CmdBtnClickHandler implements ClickHandler {

		public void onClick(ClickEvent event) {

			Button button = (Button) event.getSource();
			String text = button.getText();

			System.out.println("Tank Direction: " + text);

			if (text != null && !"".equals(text.trim())) {
				if ("go-right".equals(text)) {
					tank.setTankImage(tankImages.get("right"));
					updateTankPosition(TankDirection.RIGHT, TANK_STEP_LENGTH, 0);
				} else if ("go-left".equals(text)) {
					tank.setTankImage(tankImages.get("left"));
					updateTankPosition(TankDirection.LEFT, -TANK_STEP_LENGTH, 0);
				} else if ("go-up".equals(text)) {
					tank.setTankImage(tankImages.get("up"));
					updateTankPosition(TankDirection.UP, 0, -TANK_STEP_LENGTH);
				} else if ("go-down".equals(text)) {
					tank.setTankImage(tankImages.get("down"));
					updateTankPosition(TankDirection.DOWN, 0, TANK_STEP_LENGTH);
				} else if ("fire".equals(text)) {
					fire(tank);
				}
			}
		}

	}

	private class TankKeyDownHandler implements KeyDownHandler {

		public void onKeyDown(KeyDownEvent event) {
			if (event.isRightArrow()) {
				tank.setTankImage(tankImages.get("right"));
				updateTankPosition(TankDirection.RIGHT, TANK_STEP_LENGTH, 0);
			} else if (event.isLeftArrow()) {
				tank.setTankImage(tankImages.get("left"));
				updateTankPosition(TankDirection.LEFT, -TANK_STEP_LENGTH, 0);
			} else if (event.isDownArrow()) {
				tank.setTankImage(tankImages.get("down"));
				updateTankPosition(TankDirection.DOWN, 0, TANK_STEP_LENGTH);
			} else if (event.isUpArrow()) {
				tank.setTankImage(tankImages.get("up"));
				updateTankPosition(TankDirection.UP, 0, -TANK_STEP_LENGTH);
			} else if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
				fire(tank);
			}
		}

	}

	private void updateTankPosition(TankDirection direction, int x_pos,
			int y_pos) {
		int org_x = this.tank.getX();
		int org_y = this.tank.getY();

		this.tank.setTankDirection(direction);
		this.tank.setX(org_x + x_pos);
		this.tank.setY(org_y + y_pos);
	}

	private void fire(Tank tank) {
		tank.addBullet(produceAbullet(tank));
	}

	private TankBullet produceAbullet(Tank tank) {
		TankBullet bullet = new TankBullet();
		bullet.setBulletDirection(tank.getTankDirection());
		if (bullet.getBulletDirection().equals(TankDirection.RIGHT)) {
			bullet.setX(tank.getX() + 5);
			bullet.setY(tank.getY());
		} else if (bullet.getBulletDirection().equals(TankDirection.LEFT)) {
			bullet.setX(tank.getX() - 5);
			bullet.setY(tank.getY());
		} else if (bullet.getBulletDirection().equals(TankDirection.UP)) {
			bullet.setX(tank.getX());
			bullet.setY(tank.getY() + 5);
		} else {
			bullet.setX(tank.getX());
			bullet.setY(tank.getY() - 5);
		}

		return bullet;
	}

	private void reDrawBullet() {
		Vector<TankBullet> tankBullets = this.tank.getTankBullets();
		OUT: for (TankBullet bullet : tankBullets) {
			for (Tank badTank : badTanks) {
				if (bullet.hitTank(badTank)) {
					badTank.setHit(true);
					badTank.onFire(absolutePanel);
					tankBullets.remove(bullet);
					continue OUT;
				}
			}

			if (bullet.isBulletOutOfBound()) {
				tankBullets.remove(bullet);
				continue;
			}

			if (bullet.getBulletDirection().equals(TankDirection.RIGHT)) {
				bullet.setX(bullet.getX() + BULLET_STEP_LENGTH);
			} else if (bullet.getBulletDirection().equals(TankDirection.LEFT)) {
				bullet.setX(bullet.getX() - BULLET_STEP_LENGTH);
			} else if (bullet.getBulletDirection().equals(TankDirection.UP)) {
				bullet.setY(bullet.getY() - BULLET_STEP_LENGTH);
			} else {
				bullet.setY(bullet.getY() + BULLET_STEP_LENGTH);
			}
			bullet.drawSelf(absolutePanel);

			for (Tank badTank : badTanks) {
				if (badTank.isHit()) {
					fireCount++;
					if (fireCount == 3) {
						fireCount = 0;
						badTank.resetFire(absolutePanel);
						badTanks.remove(badTank);
					}
					continue;
				}
			}
		}
	}

	private void reDrawAllBadTanksBullet() {

		for (Tank badTank : badTanks) {
			Vector<TankBullet> tankBullets = badTank.getTankBullets();
			for (TankBullet bullet : tankBullets) {

				if (!gameover) {
					if (bullet.hitTank(tank)) {
						tank.setHit(true);
						absolutePanel.remove(tank.getTankImage());
						absolutePanel.add(fireImage, tank.getX(), tank.getY());
						break;
					}
				}
					
				if (bullet.isBulletOutOfBound()) {
					tankBullets.remove(bullet);
					continue;
				}
				if (bullet.getBulletDirection().equals(TankDirection.RIGHT)) {
					bullet.setX(bullet.getX() + BULLET_STEP_LENGTH);
				} else if (bullet.getBulletDirection().equals(
						TankDirection.LEFT)) {
					bullet.setX(bullet.getX() - BULLET_STEP_LENGTH);
				} else if (bullet.getBulletDirection().equals(TankDirection.UP)) {
					bullet.setY(bullet.getY() - BULLET_STEP_LENGTH);
				} else {
					bullet.setY(bullet.getY() + BULLET_STEP_LENGTH);
				}
				bullet.drawSelf(absolutePanel);

				if (!gameover) {
					if (tank.isHit()) {
						fireCount ++;
						if (fireCount == 6) {
							absolutePanel.remove(fireImage);
							gameover = true;
							fireCount = 0;
						}
					}
				}
			}
		}
	}

	private void reDrawTank() {
		this.tank.drawSelf(absolutePanel);
	}

	private void reDrawBadTanks() {
		for (Tank tank : badTanks) {

			if (tank.isHit()) {
				continue;
			}

			if (tank.isTime2Shoot()) {
				tank.addBullet(produceAbullet(tank));
			}

			if (tank.need2ChangeDirection4Boundaries()) {
				tank.drawSelf(absolutePanel);
				continue;
			}

			if (tank.need2ChangeDirection()) {
				TankDirection td = TankDirection.getRandomTankDirection();
				tank.setTankImage(tank.getBadTankImages().get(td.toString()));
				tank.setTankDirection(td);
				tank.setInternalCount(0);
				tank.drawSelf(absolutePanel);
				continue;
			}
			if (tank.getTankDirection().equals(TankDirection.RIGHT)) {
				tank.setX(tank.getX() + 5);
			} else if (tank.getTankDirection().equals(TankDirection.LEFT)) {
				tank.setX(tank.getX() - 5);
			} else if (tank.getTankDirection().equals(TankDirection.UP)) {
				tank.setY(tank.getY() - 5);
			} else {
				tank.setY(tank.getY() + 5);
			}
			tank.setInternalCount(tank.getInternalCount() + 1);
			tank.drawSelf(absolutePanel);
		}

	}

	private void reDraw() {
		absolutePanel.clear();
		if (!gameover)
			reDrawTank();
		reDrawBadTanks();
		if (!gameover)
			reDrawBullet();
		reDrawAllBadTanksBullet();
	}

}
